package model

import (
	"errors"
	"fmt"
	"github.com/go-gin-admin/global"
	"github.com/jinzhu/gorm"
)

type IRole interface {
	AddRole() (int64, error)                                          // 添加角色
	GetRoleList(pageIndex int64, pageSize int64) (interface{}, error) // 获取角色列表
	UpdateRole() error                                                // 更新角色
	DeleteRole(roleId int64) error                                    // 删除角色
	GetAllRoleList() (interface{}, error)                             // 获取所有角色
}

type Role struct {
	RoleInfo
	MenuIds    []int64   `json:"menuIds"`    // 菜单id集合
}

type RoleInfo struct {
	RoleId     int64     `json:"roleId" gorm:"primary_key"`     // 角色id
	RoleName   string    `json:"roleName"`   // 角色名称
	RoleKey    string    `json:"roleKey"`    // 角色key
	Sort       int64     `json:"sort"`       // 排序
	Remark     string    `json:"remark"`     // 备注
	DelFlag    string    `json:"delFlag" gorm:"default:'0'"`    // 删除标识符

	BaseModel
}

// 添加角色
func (r *Role) AddRole() (int64, error) {
	// 判断是否存在角色
	row, err := getRoleRowByRoleName(r.RoleName, "0")
	if err != nil {
		panic(err.Error())
		fmt.Println(err.Error())
	}
	// 当RoleId等于0，说明没有
	if row.RoleId > 0 {
		return 0, errors.New("已存在角色")
	}
	// 开启事务
	tx := global.GDB.Begin()
	// 构建角色表信息
	role := RoleInfo{
		RoleId:    r.RoleId,
		RoleName:  r.RoleName,
		RoleKey:   r.RoleKey,
		Sort:      r.Sort,
		Remark:    r.Remark,
		DelFlag:   r.DelFlag,
		BaseModel: BaseModel{
			CreateTime: r.CreateTime,
			UpdateTime: r.UpdateTime,
		},
	}
	if err = tx.Table("sys_role").Create(&role).Error; err != nil {
		tx.Rollback()
		return r.RoleId, err
	}
	var menus []Menu
	// 根据插入的部门ids获取所有部门列表-接口列表
	if err = tx.Table("sys_menu").Where("menu_id in (?)", r.MenuIds).Where("menu_type = ?", "A").Select("menu_id, menu_name, title, icon, path, paths, menu_type, action, parent_id, no_cache, breadcrumb, component, sort, visible, is_iframe, create_time, update_time, del_flag").Find(&menus).Error; err != nil {
		tx.Rollback()
		return r.RoleId, err
	}


	// 插入casbin_rule表，权限控制
	sqlStr1 := "INSERT INTO casbin_rule (p_type, v0, v1, v2) VALUES "
	for i := 0; i<len(menus);i++  {
		sqlStr1 += fmt.Sprintf("('%s', '%s', '%s', '%s'),", "p", r.RoleKey, menus[i].Path, menus[i].Action)
	}
	sqlStr1 = sqlStr1[0:(len(sqlStr1) - 1)]
	if err := tx.Exec(sqlStr1).Error; err != nil {
		tx.Rollback()
		return r.RoleId, err
	}

	// 批量插入角色-菜单表
	// 拼接sql， 需要使用原生方式，gorm不支持批量插入
	sqlStr := "INSERT INTO sys_role_menu (role_id, menu_id, role_name, role_key) VALUES "
	for i := 0; i < len(r.MenuIds); i++ {
		sqlStr += fmt.Sprintf("(%d, %d, '%s', '%s'),", role.RoleId, r.MenuIds[i], r.RoleName, r.RoleKey)
	}
	sqlStr = sqlStr[0:(len(sqlStr) - 1)]
	if err = tx.Exec(sqlStr).Error; err != nil {
		tx.Rollback()
		return r.RoleId, err
	}
	tx.Commit()
	return r.RoleId, nil
}

func (r *Role) GetRoleList(pageIndex int64, pageSize int64) (interface{}, error) {
	var roles []Role
	var total int64
	table := global.GDB.Table("sys_role")
	table = table.Select("role_id, role_name, role_key, sort, remark, create_time, update_time, del_flag")
	table = table.Where("del_flag = ?", "0")
	if err := table.Count(&total).Error; err != nil {
		return roles, err
	}
	table = table.Offset((pageIndex - 1) * pageSize).Limit(pageSize)
	if err := table.Find(&roles).Error; err != nil {
		return roles, err
	}

	m := make(map[string]interface{})
	m["item"] = roles
	m["total"] = total
	return m, nil
}

func (r *Role) GetAllRoleList() (interface{}, error) {
	var roles []Role
	table := global.GDB.Table("sys_role")
	table = table.Select("role_id, role_name, role_key, sort, remark, create_time, update_time, del_flag")
	table = table.Where("del_flag = ?", "0")
	if err := table.Find(&roles).Error; err != nil {
		return roles, err
	}

	m := make(map[string]interface{})
	m["item"] = roles
	return m, nil
}

func (r *Role) UpdateRole() error {
	tx := global.GDB.Begin()
	// 更新角色表 sqlStr := "UPDATE sys_role SET role_name = ?, role_key = ?, sort = ?, remark = ?, update_time = ? WHERE role_id = ?"
	table := tx.Table("sys_role")
	table = table.Where("role_id = ?", r.RoleId)
	if err := table.Updates(map[string]interface{}{"role_name": r.RoleName, "role_key": r.RoleKey, "sort": r.Sort, "remark": r.Remark, "update_time": r.UpdateTime}).Error; err != nil {
		tx.Rollback()
		return err
	}
	// 删除角色拥有的菜单 sqlStr := "DELETE FROM sys_role_menu WHERE role_id = ?"
	table1 := tx.Table("sys_role_menu")
	if err := table1.Delete(&r).Error; err != nil {
		tx.Rollback()
		return err
	}

	// 批量插入角色-菜单表
	sqlStr := "INSERT INTO sys_role_menu (role_id, menu_id, role_name, role_key) VALUES "
	for i := 0; i < len(r.MenuIds); i++ {
		sqlStr += fmt.Sprintf("(%d, %d, '%s', '%s'),", r.RoleId, r.MenuIds[i], r.RoleName, r.RoleKey)
	}
	sqlStr = sqlStr[0:(len(sqlStr) - 1)]
	if err := tx.Exec(sqlStr).Error; err != nil {
		tx.Rollback()
		return err
	}

	// 获取当前菜单id的所有的接口菜单
	var menus []Menu
	// 根据插入的部门ids获取所有部门列表-接口列表
	if err := tx.Table("sys_menu").Where("menu_id in (?)", r.MenuIds).Where("menu_type = ?", "A").Select("menu_id, menu_name, title, icon, path, paths, menu_type, action, parent_id, no_cache, breadcrumb, component, sort, visible, is_iframe, create_time, update_time, del_flag").Find(&menus).Error; err != nil {
		tx.Rollback()
		return err
	}
	// 删除当前roleKey所有的接口权限  casbin_rule
	sqlStr2 := "DELETE FROM casbin_rule WHERE v0 = ?"
	if err := tx.Exec(sqlStr2, r.RoleKey).Error; err != nil {
		tx.Rollback()
		return err
	}
	// 增加roleKey的接口权限 casbin_rule
	if len(menus) > 0 {
		sqlStr1 := "INSERT INTO casbin_rule (p_type, v0, v1, v2) VALUES "
		for i := 0; i<len(menus);i++  {
			sqlStr1 += fmt.Sprintf("('%s', '%s', '%s', '%s'),", "p", r.RoleKey, menus[i].Path, menus[i].Action)
		}
		sqlStr1 = sqlStr1[0:(len(sqlStr1) - 1)]
		if err := tx.Exec(sqlStr1).Error; err != nil {
			tx.Rollback()
			return err
		}
	}

	tx.Commit()
	return nil
}

// 删除角色
func (r *Role) DeleteRole(roleId int64) error {
	table := global.GDB.Table("sys_role")
	table = table.Where("role_id = ?", roleId)
	if err := table.Update("del_flag", "1").Error; err != nil {
		return err
	}
	return nil
}

// 根据角色名称获取当前角色
func getRoleRowByRoleName(roleName string, state string) (Role, error) {
	var role Role
	table := global.GDB.Table("sys_role")
	table = table.Where("role_name = ?", roleName)
	table = table.Where("del_flag = ?", state)
	if err := table.Select("role_id, role_name, role_key, sort, remark, create_time, update_time, del_flag").Find(&role).Error; err != nil {
		// 判断错误
		switch {
		case err == gorm.ErrRecordNotFound:
			// 说明没有找到
			return role, nil
		case err != nil:
			return role, err
		}
	}
	return role, nil
}

